/*
 *  I2C API
 *
 *  This is a very basic I2C library.
 *  Large portions were lifted from the Zilog application notes
 *  and examples. Its not very well thought out or debugged yet. 
 *  It works for some things I have tested it with, like a DS1307.
 *
 *  Its not nice and tidy the way I think it should be but it
 *  does work and it gets the job done.
 *
 *  So far this ONLY works with 7-bit addresses. I don't have any
 *  10-bit address I2C devices to play with. 
 *
 */


#include <zneo.h>
#include <stdlib.h>
#include <stdarg.h>

#include "i2c.h"


/*
 * Initialize I2C master
 */

void i2c_init()
{

    // Set baud rate
    // BRG = systemclock/(4 * baudrate)

    I2CBR = 0x1C;                     // about 48,000 @ 5.5296 MHz

    PAAF |= 0xC0;                    // Enable I2C alternate function

    I2CMODE = 0x00;                   // polling, 7bit, auto ack
                                      // no GCE, no slave addr
                                      // no diags

    I2CCTL = I2CCTL_ENABLE;           // Enable Rx/Tx; 

}


/*
 * Wait for transmission to complete
 */ 

void i2c_wait_TDRE() {
    int timeout = 0;
    while((I2CISTAT & TRANSMIT_DATA_REG_EMPTY) == 0x00) {
        //if(++timeout == TIMEOUT)
        //break;
        ;
    }
}


/* 
 * Wait for data to arrive.
 */

void i2c_wait_RDRF() {
    int timeout = 0;
    while((I2CISTAT & RECEIVE_DATA_REG_FULL) == 0x00) {
        //if(++timeout == TIMEOUT)
        //break;
        ;
    }
}


/*
 * Flush the buffer and toggle the I2C on and off.
 */

void i2c_clear() {
    I2CCTL |=  I2CCTL_FLUSH;
    I2CCTL &= ~I2CCTL_ENABLE;
    I2CCTL |=  I2CCTL_ENABLE;
}


/*
 * Write 1 byte to device.
 */

void i2c_write_byte( int addr, int data ) { 
    I2CDATA = addr;
    I2CCTL |= I2CCTL_START;
    i2c_wait_TDRE();

    I2CDATA = data;
    i2c_wait_TDRE();

    I2CCTL |= I2CCTL_STOP;
}


/*
 * Read a byte from a device
 */ 

unsigned char i2c_read_byte(int addr) { 
    unsigned char data=0;

    I2CDATA = addr;
    I2CCTL |= I2CCTL_START;
    I2CCTL |= I2CCTL_NACK;
    i2c_wait_RDRF();
            
    data = I2CDATA;
    I2CCTL |= I2CCTL_STOP;

    return data;
}

unsigned short i2c_read_short(int addr) { 
    unsigned char msb=0;
	unsigned char lsb=0;
	unsigned short ret = 0x0000;

	// repeat start by master
	// address byte
    I2CDATA = addr;
    I2CCTL |= I2CCTL_START;
	// ack by device


	// most significant byte
	// ack by master
    i2c_wait_RDRF();
    msb = I2CDATA;

	// least significant byte
	// no ack by master
	I2CCTL |= I2CCTL_NACK;
    i2c_wait_RDRF();
    lsb = I2CDATA;
	
	// stop cond by master
    I2CCTL |= I2CCTL_STOP;

	ret = msb<<8 | lsb;

    return ret;
}

/* 
 * Send a sequence of I2C bytes 
 * with the first byte, set the START condition 
 * and with the last byte set the STOP condition
 */

void i2c_write_bytes( int n, ... ) { 
    va_list ap;
    int i=0;
    int data=0;

    va_start(ap, n);                // prepare vararg list

    i2c_wait_TDRE();                // wait for any previous transaction

    for (i = 0; i < n; i++) {

        data = va_arg(ap, int);     // get vararg n
        I2CDATA = data;             // send it

        if (i==0)                   // first byte? set STOP condition
            I2CCTL |= I2CCTL_START;

        i2c_wait_TDRE();            // wait for byte to go

        if (i==(n-1))               // last byte? set STOP condition
            I2CCTL |= I2CCTL_STOP;

    }
    va_end(ap);
    
}

